package com.listeningframework.boot.autoconfigure.orm.jpa.support;

import com.listeningframework.boot.autoconfigure.orm.jpa.search.SpecBuilder;
import com.listeningframework.boot.autoconfigure.orm.jpa.search.Specifications;
import com.listeningframework.boot.commons.math.NumberUtils;
import com.listeningframework.boot.domains.Executable;
import com.listeningframework.boot.domains.Pagination;
import com.listeningframework.boot.domains.Paging;
import com.listeningframework.boot.exception.BusinessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.EntityManagerFactory;
import javax.persistence.criteria.CriteriaBuilder;
import java.io.Serializable;

/**
 * Listening JPA Service
 *
 * @author lixining
 * @version $Id: ListeningJpaService.java, v 0.1 2016年2月23日 下午3:29:57 lixining Exp $
 */
public abstract class ListeningJpaService<T, ID extends Serializable> {
    /**
     * 日志
     */
    protected static final Logger logger = LoggerFactory.getLogger(ListeningJpaService.class);
    /**
     * Entity Manager Factory
     */
    @Autowired
    protected EntityManagerFactory entityManagerFactory;

    /**
     * Get CriteriaBuilder
     *
     * @return CriteriaBuilder
     */
    protected CriteriaBuilder getCriteriaBuilder() {
        return entityManagerFactory.getCriteriaBuilder();
    }

    /**
     * Convert Specification
     *
     * @param builder SpecBuilder
     * @param cls     参数对象类型
     * @return Specification
     */
    protected Specification<T> convertSpec(SpecBuilder builder, Class<T> cls) {
        return new Specifications<T>(builder.build(), cls);
    }

    /**
     * Convert Example
     *
     * @param t       Example类型
     * @param execute 执行回调
     * @return Example
     * @throws BusinessException 转换异常
     */
    protected Example<T> convertExample(T t, Executable<ExampleMatcher> execute) throws BusinessException {
        ExampleMatcher matcher = ExampleMatcher.matching();
        execute.execute(matcher);
        return Example.of(t, matcher);
    }

    /**
     * 构建PageRequest对象
     *
     * @param page      分页参数
     * @param direction 排序类型
     * @param property  排序属性
     * @return PageRequest
     */
    protected PageRequest convertPageRequest(Paging page, Direction direction, String... property) {
        Integer pageNo = page.getPageNo() - 1;
        Integer pageSize = page.getPageSize();
        return new PageRequest(pageNo, pageSize, new Sort(direction, property));
    }

    /**
     * 转换Page对象为Pagination
     *
     * @param pagination 原始分页
     * @return Pagination 目标分页
     */
    protected Pagination<T> convertPagination(Page<T> pagination) {
        Pagination<T> result = new Pagination<T>();
        result.setTotalCount(0);
        if (pagination == null || pagination.getTotalElements() == 0) {
            return result;
        }
        long totalCount = pagination.getTotalElements();
        result.setTotalCount(NumberUtils.longToInt(totalCount));
        result.addAllData(pagination.getContent());
        return result;
    }

    /**
     * 对象对拷
     *
     * @param src  源目标
     * @param dest 对象目标
     * @param <SRC> 源类型
     * @param <DEST> 目标类型
     */
    protected <SRC, DEST> void copyUpdateFields(SRC src, DEST dest) {
        BeanUtils.copyProperties(src, dest, "id", "createdDate", "createdBy");
    }
}